﻿using SCG.General;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

//Defines how a dungeon level should be built
//Inputs - Difficulty Level

//TODO move dressing into MapObjectFactory

namespace TTEngine
{
    public class DungeonTheme
    {
        //static constructor
        static DungeonTheme()
        {
            //get a list of room and corridor templates
            TypeLoader roomLoader = new TypeLoader("Room", "");
            foreach (Type t in roomLoader.Types)
            {
                Room room = (Room)roomLoader.CreateInstance(t);
                if (room.IsCorridor)
                    _corridorTemplates.Add(room);
                else if (!room.IsEntrance)
                    _roomTemplates.Add(room);
            }
        }

        //constructor
        public DungeonTheme(int difficultyLevel)
        {
            _difficultyLevel = difficultyLevel;
        }

        //public properties
        public DungeonLevel Level { get; set; }

        public int DifficultyLevel
        {
            get
            {
                return _difficultyLevel;
            }
        }

        //return a list containing a number of lists of rooms.  Each list is a group of rooms that
        //should be placed relatively closely together.
        //TODO provide more variability in layouts
        public List<List<Room>> RoomDecks
        {
            get
            {
                Faction _currentFaction = null;
                _candidateRooms.Clear();
                _candidateCorridors.Clear();
                foreach (Room room in _roomTemplates)
                    if (room.DifficultyLevel <= _difficultyLevel)
                        _candidateRooms.Add(room);
                foreach (Room corridor in _corridorTemplates)
                    if (corridor.DifficultyLevel <= _difficultyLevel)
                        _candidateCorridors.Add(corridor);

                List<List<Room>> decks = new List<List<Room>>();
                int numDecks = Globals.RandomIntFromTo(2, 4);
                while (numDecks > 0)
                {
                    List<Room> rooms = new List<Room>();
                    int numRooms = Globals.RandomIntFromTo(7, 14);
                    while (numRooms > 0 && _candidateRooms.Count > 0)
                    {
                        Room room = _candidateRooms.RandomValue;
                        room = room.Clone();

                        //avoid rooms flagged as too difficult in and of themselves
                        if (room.DifficultyLevel > _difficultyLevel)
                            continue;

                        //does the room need a faction?
                        if (room.FactionTags.Count > 0)
                        {
                            //yes, do we have a faction?
                            if (_currentFaction == null)
                            {
                                //no, get a faction that can use this room if possible
                                _currentFaction = FactionFactory.GetFactionForRoom(room, _difficultyLevel);
                                if (_currentFaction == null)
                                    continue;
                                room.Faction = _currentFaction;
                            }
                            else
                            {
                                //can the current faction use this room?  Also give a chance to spawn a new faction
                                if (_currentFaction.CanUseRoom(room) && Globals.RandomIntFromTo(1, 10) < 10)
                                {
                                    //yes, remember the faction so it can spawn monsters during post-processing
                                    room.Faction = _currentFaction;
                                }
                                else
                                {
                                    //no, should we spawn a new faction?
                                    if (Globals.RandomIntFromTo(1, 6) == 6)
                                    {
                                        _currentFaction = FactionFactory.GetFactionForRoom(room, _difficultyLevel);
                                        if (_currentFaction == null)
                                            continue;
                                        room.Faction = _currentFaction;
                                    }
                                }
                            }
                        }

                        room.Level = Level;
                        numRooms--;
                        rooms.Add(room);
                    }
                    decks.Add(rooms);
                    numDecks--;
                }
                return decks;
            }
        }

        //generate an entrance
        public Room GenerateEntrance()
        {
            return new Entrance();
        }

        //generate a corridor
        public Room GenerateCorridor()
        {
            Room corridor = _candidateCorridors.RandomValue;
            corridor = corridor.Clone();
            return corridor;
        }

        //generate a door
        public Door GenerateDoor()
        {
            return new Door();
        }

        //generate a piece of loot, can also be a piece of mundane gear (level=0)
        public MapObject GenerateLoot()
        {
            return MapObjectFactory.GetLoot(_difficultyLevel);
        }

        //generate some dungeon dressing
        public MapObject GenerateDressing()
        {
            return MapObjectFactory.GetDressing(_difficultyLevel);
        }

        //post build processing 
        public virtual void PostProcessingPreCorridorRemoval()
        {
        }
        public virtual void PostProcessingPostCorridorRemoval()
        {
            CharMatrix doorMap = new CharMatrix("XXXX,-XX-,-XX-");
            PerformDoorScan(doorMap, SCGOrientation.Horizontal, 1, 1);
            doorMap = doorMap.Twist();
            PerformDoorScan(doorMap, SCGOrientation.Vertical, 1, 1);
            doorMap = doorMap.Twist();
            PerformDoorScan(doorMap, SCGOrientation.Horizontal, 1, 1);
            doorMap = doorMap.Twist();
            PerformDoorScan(doorMap, SCGOrientation.Vertical, 1, 1);

            //create a number of corridor doors
            doorMap = new CharMatrix("-XX-,-XX-,-XX-,-XX-");
            PerformCorridorDoorScan(doorMap, SCGOrientation.Horizontal, 1, 2);
            doorMap = doorMap.Twist();
            PerformCorridorDoorScan(doorMap, SCGOrientation.Vertical, 1, 1);

            //TODO create traps?
        }

        //scan for doors
        public void PerformDoorScan(CharMatrix map, SCGOrientation orientation, int xOffset, int yOffset)
        {
            SCGPoint pt = Level.TerrainMap.FindPattern(map, 0, 0, true);
            while (pt != null)
            {
                pt = Level.TerrainMap.FindPattern(map, pt.X, pt.Y, false);
                if (pt != null && Globals.Random.NextDouble() > 0.05)
                {
                    Door door = GenerateDoor();
                    door.Orientation = orientation;
                    Level.AddObjectToMap(door, pt.X + xOffset, pt.Y + yOffset);
                }
            }
        }
        public void PerformCorridorDoorScan(CharMatrix map, SCGOrientation orientation, int xOffset, int yOffset)
        {
            int numDoors = Globals.RandomIntFromTo(1, 10);
            while (numDoors > 0)
            {
                int x = Globals.RandomIntFromTo(0, Level.Width);
                int y = Globals.RandomIntFromTo(0, Level.Height);
                SCGPoint pt = Level.TerrainMap.FindPattern(map, x, y, true);
                if (pt != null)
                {
                    Door door = GenerateDoor();
                    door.Orientation = orientation;
                    Level.AddObjectToMap(door, pt.X + xOffset, pt.Y + yOffset);
                }
                numDoors--;
            }
        }

        //expose graphics to use for tiles and walls
        public virtual Bitmap TileGraphic
        {
            get
            {
                return Properties.Resources.Tiles64x64;
            }
        }
        public virtual Bitmap WallGraphic
        {
            get
            {
                return Properties.Resources.StoneWall;
            }
        }

        //private members
        private int _difficultyLevel;
        private static List<Room> _roomTemplates = new List<Room>();
        private static List<Room> _corridorTemplates = new List<Room>();
        private RandomList<Room> _candidateRooms = new RandomList<Room>();
        private RandomList<Room> _candidateCorridors = new RandomList<Room>();
    }
}
